package nats_micro

import (
	"context"
	"dcim/pkg/generic-go-kit/core"
	"dcim/pkg/generic-go-kit/core/coder"
	"encoding/json"
	"fmt"
	"github.com/nats-io/nats.go/micro"
)

type NatsMicroHandlerI interface {
	RegisterHandlers(ms micro.Service, options ...NatsMicroHandlerOption)
}

type NatsMicroHandler struct {
	Env       string
	Endpoints core.EndpointCollector
}

func (h *NatsMicroHandler) RegisterHandlers(ms micro.Service, options ...NatsMicroHandlerOption) {
	qName := h.Env + "." + ms.Info().Name
	group := ms.AddGroup(qName, micro.WithGroupQueueGroup(qName))

	fmt.Println("Service Name: " + ms.Info().Name)
	fmt.Println("Env: " + h.Env)
	fmt.Println("Queue Name: " + qName)
	fmt.Println("Group Name: " + qName)
	options = append(options, core.GetContextFromNatsMicroRequest)
	//options = append(options, gktcore.DefaultErrorEncoder)

	for _, en := range h.Endpoints.GetEndpointNodesMapper() {
		t := en.GenerateNatsMicroTransport()
		handler := MakeNatsMicroHandler(
			en,
			t.MakeDecodeRequestFunc(),
			options...,
		)
		requestSchema, err := json.Marshal(en.Request())
		if err != nil {
			requestSchema = []byte("{}")
		}

		responseSchema, err := json.Marshal(en.Response())
		if err != nil {
			responseSchema = []byte("{}")
		}

		metadata := map[string]string{
			"request_schema":  string(requestSchema),
			"response_schema": string(responseSchema),
		}

		err = group.AddEndpoint(en.Name(), handler, micro.WithEndpointMetadata(metadata))
		if err != nil {
			panic(err)
		}
		fmt.Println("Register Endpoint: " + en.Name())

	}

}

type NatsMicroHandlerOption func(ctx context.Context, request micro.Request) context.Context

func MakeNatsMicroHandler(endpoint core.EndpointMaker, dec coder.DecodeNatsMicroRequestFunc, options ...NatsMicroHandlerOption) micro.Handler {
	ctx, cancel := context.WithCancel(context.Background())

	h := micro.ContextHandler(ctx, func(ctx context.Context, request micro.Request) {

		for _, option := range options {
			ctx = option(ctx, request)
		}

		req, err := dec(ctx, request)
		if err != nil {
			request.Error("500", "system error", []byte{})
			return
		}

		response, err := endpoint.Endpoint()(ctx, req)
		if err != nil {
			request.Error("500", "system error", []byte{})
			return
		}

		err = request.RespondJSON(response)
		if err != nil {
			return
		}

		defer cancel()

	})
	return h
}
